package com.shadow.repository;

import com.mysema.query.jpa.impl.JPAQuery;
import com.mysema.query.types.Expression;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.expr.NumberExpression;
import com.mysema.query.types.path.EntityPathBase;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;

public class MyCustomRepositoryImpl<T, ID extends Serializable> extends
        SimpleJpaRepository<T, ID> implements MyCustomRepository<T, ID> {

    private final EntityManager entityManager;

    public MyCustomRepositoryImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        entityManager = em;
    }

    public MyCustomRepositoryImpl(
            final JpaEntityInformation<T, ?> entityInformation,
            final EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
    }

    @Override
    public List<T> queryDsl(EntityPathBase<T> entityToSelect, Predicate... o) {
        JPAQuery query = new JPAQuery(entityManager);
        return query.from(entityToSelect).where(o).list(entityToSelect);
    }

    public List<T> queryDsl(EntityPathBase<T> entityToSelect, int start, int limit, Predicate... o) {
        JPAQuery query = new JPAQuery(entityManager);
        return query.from(entityToSelect).offset(start).limit(limit).where(o).list(entityToSelect);
    }

    public List<T> queryGroupBy(EntityPathBase<T> entityToSelect, OrderSpecifier<?> order, Expression<?> groupExpression, Predicate... o) {
        JPAQuery query = new JPAQuery(entityManager);
        return query.from(entityToSelect).where(o).groupBy(groupExpression).orderBy(order).list(entityToSelect);
    }

    @Override
    public T querySingle(EntityPathBase<T> entityToSelect, Predicate... o) {
        JPAQuery query = new JPAQuery(entityManager);

        return query.from(entityToSelect).limit(1).where(o).uniqueResult(entityToSelect);
    }

    public T querySingle(EntityPathBase<T> entityToSelect,
                         OrderSpecifier<?> order, Predicate... o) {
        JPAQuery query = new JPAQuery(entityManager);
        return query.from(entityToSelect).where(o).orderBy(order)
                .limit(1).uniqueResult(entityToSelect);
    }

    @Override
    public List<T> queryDslWithOrder(EntityPathBase<T> entityToSelect,
                                     OrderSpecifier<?> order, Predicate... o) {
        JPAQuery query = new JPAQuery(entityManager);
        return query.from(entityToSelect).where(o).orderBy(order)
                .list(entityToSelect);
    }

    @Override
    public List<T> queryDslWithOrderAndLimit(EntityPathBase<T> entityToSelect,
                                             OrderSpecifier<?> order, int limitNum, Predicate... o) {
        JPAQuery query = new JPAQuery(entityManager);
        return query.from(entityToSelect).where(o).orderBy(order)
                .limit(limitNum).list(entityToSelect);
    }

    public List<T> queryDslWithOrderAndLimit(EntityPathBase<T> entityToSelect,
                                             OrderSpecifier<?> order, int limitNum) {
        JPAQuery query = new JPAQuery(entityManager);
        return query.from(entityToSelect).orderBy(order).limit(limitNum).list(entityToSelect);
    }

    public BigDecimal querySumResult(EntityPathBase<T> entityToSelect,
                                     NumberExpression<BigDecimal> numberExpression, Predicate... o) {
        JPAQuery query = new JPAQuery(entityManager);

        return query.from(entityToSelect).where(o)
                .singleResult(numberExpression);
    }

    public List<T> queryDslWithOrder(EntityPathBase<T> entityToSelect,
                                     OrderSpecifier<?> order, int offset, int limitNum) {
        JPAQuery query = new JPAQuery(entityManager);
        return query.from(entityToSelect).orderBy(order).offset(offset).limit(limitNum).list(entityToSelect);
    }

    public T merge(T entity) {
        return entityManager.merge(entity);
    }

    @Override
    public List<Object[]> queryByJPQL(String jpql, int startIndex, int size) {
        TypedQuery<Object[]> query = this.entityManager.createQuery(jpql, Object[].class);
        query.setFirstResult(startIndex);
        query.setMaxResults(size);
        return query.getResultList();
    }

    @Override
    public Long queryCountByJPQL(String jpql) {
        TypedQuery<Long> query = this.entityManager.createQuery(jpql, Long.class);
        return query.getSingleResult();
    }

    public void persist(Object object) {
        entityManager.persist(object);
    }

}
